﻿Imports System
Imports System.Net
Imports System.Net.sockets
Imports System.Threading

Namespace TCP

    Public Class ServerSocket

        Protected _serverSocket As Socket                 '服务器Socket
        Protected _maxClient As Integer = 100             '最大连接数
        Protected _clientList As List(Of ClientInfo)      '客户端Socket列表
        Protected _curClientCount As Integer = 0          '当前客户端连接数
        Protected _acceptThread As Thread                 '接收连接线程
        Protected _isAccept As Boolean                    '是否接收连接
        Protected _isReceive As Boolean                   '是否接收数据
        'Protected _receiveThread As Thread                '接收数据线程
        Protected _heartIdleTime As Integer               '心跳包接受发送时间
        Protected _heartWaitTime As Integer               '心跳包接受等待服务器响应间隔
        Protected _heartRep As Integer                    '等待服务器次数
        Protected _heartDataClient() As Byte = Text.Encoding.ASCII.GetBytes("Hearting") '客户端心跳包数据
        Protected _heartDataServer() As Byte = _heartDataClient '服务端心跳包数据
        Protected _receiveBufferSize As Integer = 512
        Protected _sendBufferSize As Integer = 512
        Protected _receiveTimeout As Integer = 0
        Protected _sendTimeout As Integer = 0


        Public Event OnAccept As EventHandler(Of AcceptEventArgs)
        Public Event OnMaxClient As EventHandler
        Public Event OnReceive As EventHandler(Of ReceiveEventArgs)
        Public Event OnClientDisconnect As EventHandler(Of DisconnectArgs)
        Public Event OnFreeTime As EventHandler
        Public Event OnHeartTimeout As EventHandler


#Region "Properties"
        ''' <summary>
        ''' 服务器端Socket对象
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Socket() As Socket
            Get
                Return _serverSocket
            End Get
        End Property
        ''' <summary>
        ''' 数据接收缓冲区大小(字节)。默认512字节。
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property ReceiveBufferSize() As Integer
            Get
                Return _receiveBufferSize
            End Get
            Set(ByVal value As Integer)
                _receiveBufferSize = value
            End Set
        End Property

        ''' <summary>
        ''' 数据发送缓冲区大小(字节)。默认512字节。
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property SendBufferSize() As Integer
            Get
                Return _sendBufferSize
            End Get
            Set(ByVal value As Integer)
                _sendBufferSize = value
            End Set
        End Property

        ''' <summary>
        ''' 数据接收超时值(毫秒)。默认值为 0，指示超时期限无限大。
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property ReceiveTimeout() As Integer
            Get
                Return _receiveTimeout
            End Get
            Set(ByVal value As Integer)
                _receiveTimeout = value
            End Set
        End Property

        ''' <summary>
        ''' 数据发送超时值(毫秒)。默认值为 0，指示超时期限无限大。
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property SendTimeout() As Integer
            Get
                Return _sendTimeout
            End Get
            Set(ByVal value As Integer)
                _sendTimeout = value
            End Set
        End Property
        ''' <summary>
        ''' 客户端信息集合
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ClientList() As List(Of ClientInfo)
            Get
                Return _clientList
            End Get
        End Property

        ''' <summary>
        ''' 服务器最大连接数。默认为100
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property MaxClientCount() As Integer
            Get
                Return _maxClient
            End Get
            Set(ByVal value As Integer)
                _maxClient = value
            End Set
        End Property

        ''' <summary>
        ''' 服务器当前连接数
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property CurrentClientCount() As Integer
            Get
                Return _curClientCount
            End Get
        End Property
        ''' <summary>
        ''' 心跳包开始发送时间(毫秒)
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property HeartIdleTime() As Integer
            Get
                Return _heartIdleTime / 10000
            End Get
            Set(ByVal value As Integer)
                _heartIdleTime = value * 10000
            End Set
        End Property

        ''' <summary>
        ''' 心跳包发送等待响应间隔(毫秒)
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property HeartWaitTime() As Integer
            Get
                Return _heartWaitTime / 10000
            End Get
            Set(ByVal value As Integer)
                _heartWaitTime = value * 10000
            End Set
        End Property

        ''' <summary>
        ''' 等待心跳包响应次数
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property HeartRep() As Integer
            Get
                Return _heartRep
            End Get
            Set(ByVal value As Integer)
                _heartRep = value
            End Set
        End Property

        ''' <summary>
        ''' 服务器端心跳包数据
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property HeartDataServer() As Byte()
            Get
                Return _heartDataServer
            End Get
            Set(ByVal value As Byte())
                _heartDataServer = value
            End Set
        End Property

        ''' <summary>
        ''' 客户端心跳包数据
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property HeartDataClient() As Byte()
            Get
                Return _heartDataClient
            End Get
            Set(ByVal value As Byte())
                _heartDataClient = value
            End Set
        End Property
#End Region

        Public Sub New()
            _serverSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            _clientList = New List(Of ClientInfo)
        End Sub


        ''' <summary>
        ''' 开启服务器
        ''' </summary>
        ''' <param name="port">服务器端口</param>
        ''' <param name="maxClient">最大客户端连接数</param>
        ''' <param name="backlog">挂起连接队列的最大长度</param>
        ''' <remarks></remarks>
        Public Function ServerStart(ByVal port As Integer, ByVal maxClient As Integer, ByVal backlog As Integer) As Boolean
            Dim ipep As New IPEndPoint(IPAddress.Any, port)

            Try
                _maxClient = maxClient

                _serverSocket.Bind(ipep)
                _serverSocket.Listen(backlog)

                _curClientCount = 0
                For i As Integer = 0 To _maxClient - 1
                    Dim client As New ClientInfo()
                    client.ServerSocket = Me
                    AddHandler client.OnReceive, AddressOf ReceiveMethod
                    AddHandler client.OnClientDisconnect, AddressOf DisconnectMethod
                    AddHandler client.OnFreeTime, AddressOf FreeTimeMethod
                    AddHandler client.OnHeartTimeout, AddressOf HeartTimeoutMethod
                    _clientList.Add(client)
                Next

                '开始接收连接
                If _acceptThread Is Nothing Then
                    _acceptThread = New Thread(AddressOf AcceptMethod)
                    _acceptThread.IsBackground = True
                    _isAccept = True
                    _acceptThread.Start()
                End If

            Catch ex As Exception
                Return False
            End Try

            Return True
        End Function

        '停止服务器
        Protected Function ServerStop() As Boolean
            Try
                _isAccept = False
                If _acceptThread IsNot Nothing Then
                    _acceptThread.Abort()
                    _acceptThread = Nothing
                End If

            Catch ex As Exception
                Return False
            End Try

            Return True
        End Function

        '接收连接线程方法
        Protected Sub AcceptMethod()
            Try
                Do While _isAccept

                    'If _clientList.Count < _maxClient Then '判断最大连接数
                    Dim clientSocket As Socket = _serverSocket.Accept()
                    Dim client As ClientInfo = Nothing

                    For Each c As ClientInfo In _clientList
                        If c.M_Socket Is Nothing Then
                            c.M_Socket = clientSocket
                            client = c
                            System.Threading.Interlocked.Increment(_curClientCount)
                            Exit For
                        End If
                    Next

                    If client Is Nothing Then
                        RaiseEvent OnMaxClient(Me, EventArgs.Empty)
                        Thread.Sleep(1000)
                        Continue Do
                    End If

                    'Dim client As New ClientInfo(clientSocket)
                    clientSocket.ReceiveBufferSize = _receiveBufferSize
                    clientSocket.SendBufferSize = _sendBufferSize
                    clientSocket.ReceiveTimeout = _receiveTimeout
                    clientSocket.SendTimeout = _sendTimeout
                    'client.ServerSocket = Me
                    'AddHandler client.OnReceive, AddressOf ReceiveMethod
                    'AddHandler client.OnClientDisconnect, AddressOf DisconnectMethod

                    '_clientList.Add(client)

                    client.StartWork()

                    Try
                        Dim ae As New AcceptEventArgs()
                        ae.Client = client
                        ae.RemoteEndPoint = clientSocket.RemoteEndPoint
                        RaiseEvent OnAccept(Me, ae)
                    Catch ex As Exception

                    End Try

                    'Else
                    ''Dim client As Socket = _serverSocket.Accept()
                    ''client.Shutdown(SocketShutdown.Both)
                    ''client.Close()
                    'RaiseEvent OnMaxClient(Me, EventArgs.Empty)
                    'Thread.Sleep(1000)
                    'End If
                Loop
            Catch ex As SocketException
                Throw ex
            End Try
        End Sub

        ''' <summary>
        ''' 发送数据至客户端
        ''' </summary>
        ''' <param name="bytes">要发送的数据</param>
        ''' <param name="client">目标客户端对象</param>
        ''' <returns>返回已发送数据的长度(字节)</returns>
        ''' <remarks></remarks>
        Public Function SendToClient(ByVal bytes() As Byte, ByVal client As ClientInfo) As Integer
            Return client.Send(bytes)
        End Function

        ''' <summary>
        ''' 发送数据至客户端
        ''' </summary>
        ''' <param name="bytes">要发送的数据</param>
        ''' <param name="length">要发送的数据长度(字节)</param>
        ''' <param name="client">目标客户端对象</param>
        ''' <returns>返回已发送数据的长度(字节)</returns>
        ''' <remarks></remarks>
        Public Function SendToClient(ByVal bytes() As Byte, ByVal length As Integer, ByVal client As ClientInfo) As Integer
            Return client.Send(bytes, length)
        End Function

        ''' <summary>
        ''' 从客户端读取接收数据
        ''' </summary>
        ''' <param name="length">读取长度(字节)</param>
        ''' <param name="client">目标客户端对象</param>
        ''' <returns>返回byte数组</returns>
        ''' <remarks></remarks>
        Public Function ReceiveFromClient(ByVal length As Integer, ByVal client As ClientInfo) As Byte()
            Return client.Receive(length)
        End Function

        '事件处理方法
        Protected Sub ReceiveMethod(ByVal sender As Object, ByVal e As ReceiveEventArgs)
            RaiseEvent OnReceive(sender, e)
        End Sub
        Protected Sub DisconnectMethod(ByVal sender As Object, ByVal e As DisconnectArgs)
            RaiseEvent OnClientDisconnect(sender, e)
        End Sub
        Protected Sub FreeTimeMethod(ByVal sender As Object, ByVal e As EventArgs)
            RaiseEvent OnFreeTime(sender, e)
        End Sub
        Protected Sub HeartTimeoutMethod(ByVal sender As Object, ByVal e As EventArgs)
            RaiseEvent OnHeartTimeout(sender, e)
        End Sub

        ''' <summary>
        ''' 关闭所有客户端连接
        ''' </summary>
        ''' <param name="timeout">等待 timeout 秒以发送所有剩余数据，然后关闭该套接字。</param>
        ''' <remarks></remarks>
        Public Sub CloseAll(ByVal timeout As Integer)
            Try
                For Each client As ClientInfo In _clientList
                    client.Close(timeout)
                    RemoveHandler client.OnReceive, AddressOf ReceiveMethod
                    RemoveHandler client.OnClientDisconnect, AddressOf DisconnectMethod
                Next
                _clientList.Clear()
            Catch ex As Exception
                Throw ex
            End Try
        End Sub

        ''' <summary>
        ''' 关闭指定客户端连接
        ''' </summary>
        ''' <param name="timeout">等待 timeout 秒以发送所有剩余数据，然后关闭该套接字。</param>
        ''' <remarks></remarks>
        Public Sub CloseClient(ByVal timeout As Integer, ByVal client As ClientInfo)
            Monitor.Enter(Me)
            Try
                'System.IO.File.AppendAllText(".\\" & Now.Month & Now.Day & ".log", "Close Client" & vbCrLf)
                System.Threading.Interlocked.Decrement(_curClientCount)
                client.Close(timeout)
                'RemoveHandler client.OnReceive, AddressOf ReceiveMethod
                'RemoveHandler client.OnClientDisconnect, AddressOf DisconnectMethod
                '_clientList.Remove(client)

                'System.GC.Collect()
            Catch ex As Exception
                Throw ex
            Finally
                Monitor.Exit(Me)
            End Try
        End Sub

        ''' <summary>
        ''' 关闭服务器Socket连接并释放所有关联的资源
        ''' </summary>
        ''' <param name="timeout">等待 timeout 秒以发送所有剩余数据，然后关闭该套接字。</param>
        ''' <remarks></remarks>
        Public Sub Close(ByVal timeout As Integer)
            'System.IO.File.AppendAllText(".\\" & Now.Month & Now.Day & ".log", "Close Server" & vbCrLf)
            Try
                ServerStop()
                CloseAll(0)
                _serverSocket.Close(timeout)
                _serverSocket = Nothing
                _clientList = Nothing
            Catch ex As Exception
                Throw ex
            End Try

            GC.Collect()
        End Sub


    End Class

    Public Enum ClientListHandlers
        ADD
        REMOVE
    End Enum


    ''' <summary>
    ''' 客户端信息类
    ''' </summary>
    ''' <remarks></remarks>
    Public Class ClientInfo

        'Protected _ID As String
        Protected _socket As Socket
        Protected _thread As Thread
        Protected _isReceiving As Boolean
        Protected _serverSocket As ServerSocket
        Protected _buffer() As Byte
        Protected _isDisconnected As Boolean = True
        Protected _isFreeTime As Boolean = False
        Protected _lastTicks As Long
        Protected _freeTicks As Long
        Protected _heartData() As Byte
        'Protected _heartCallback As New AsyncCallback(AddressOf HeartCallback)

        Public Event OnReceive As EventHandler(Of ReceiveEventArgs)
        Public Event OnClientDisconnect As EventHandler(Of DisconnectArgs)
        Public Event OnFreeTime As EventHandler
        Public Event OnHeartTimeout As EventHandler

#Region "Properties"

        'Public ReadOnly Property ID() As String
        '    Get
        '        Return _ID
        '    End Get
        'End Property

        Friend Property M_Socket() As Socket
            Get
                Return _socket
            End Get
            Set(ByVal value As Socket)
                _socket = value
            End Set
        End Property

        Public ReadOnly Property Socket() As Socket
            Get
                Return _socket
            End Get
            'Set(ByVal value As Socket)
            '    _socket = value
            'End Set
        End Property

        Public Property ServerSocket() As ServerSocket
            Get
                Return _serverSocket
            End Get
            Set(ByVal value As ServerSocket)
                _serverSocket = value
            End Set
        End Property

        Public Property IsDisconnected() As Boolean
            Get
                Return _isDisconnected
            End Get
            Set(ByVal value As Boolean)
                _isDisconnected = value
            End Set
        End Property

        ''' <summary>
        ''' 是否空闲状态
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property IsFreeTime() As Boolean
            Get
                Return _isFreeTime
            End Get
            Set(ByVal value As Boolean)
                _isFreeTime = value
            End Set
        End Property

        ''' <summary>
        ''' 最后操作时刻
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property LastTicks() As Long
            Get
                Return _lastTicks
            End Get
            Set(ByVal value As Long)
                _lastTicks = value
            End Set
        End Property

        ''' <summary>
        ''' 空闲时刻
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property FreeTicks() As Long
            Get
                Return _freeTicks
            End Get
            Set(ByVal value As Long)
                _freeTicks = value
            End Set
        End Property

        'Public Property HeartDataClient() As Byte()
        '    Get
        '        Return _heartDataClient
        '    End Get
        '    Set(ByVal value As Byte())
        '        _heartDataClient = value
        '        ReDim _heartData(value.Length - 1)
        '    End Set
        'End Property

        'Public Property HeartDataServer() As Byte()
        '    Get
        '        Return _heartDataServer
        '    End Get
        '    Set(ByVal value As Byte())
        '        _heartDataServer = value
        '    End Set
        'End Property

#End Region

        Public Sub New()

        End Sub
        Public Sub New(ByVal clientSocket As Socket)
            '_ID = System.Guid.NewGuid().ToString()
            _socket = clientSocket
            '_isDisconnected = False
        End Sub

        Friend Sub StartWork()
            If _thread Is Nothing Then
                _thread = New Thread(AddressOf WorkerThread)
                _isReceiving = True
                _isDisconnected = False
                _isFreeTime = False
                _thread.IsBackground = True
                _thread.Start()
            End If
        End Sub

        '接收数据工作线程方法
        Protected Sub WorkerThread(ByVal state As Object)
            Dim bufLen As Integer = 0
            Dim nowTicks As Long = 0
            _lastTicks = DateTime.Now.Ticks

            Do While _isReceiving

                Try
                    nowTicks = DateTime.Now.Ticks
                    Dim clientSocket As Socket = _socket

                    '检测客户端空闲状态
                    If _isFreeTime Then
                        Do While (nowTicks - _freeTicks) < ServerSocket.HeartWaitTime * 10000 * ServerSocket.HeartRep
                            If Not _isFreeTime Then Exit Do
                            nowTicks = DateTime.Now.Ticks
                            BeginHeart()
                            If _isDisconnected Then Exit Do
                            Thread.Sleep(100)
                        Loop

                        If _isFreeTime Then
                            If _isDisconnected = False Then RaiseEvent OnHeartTimeout(Me, EventArgs.Empty)
                            If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.HeartTimeout))
                        End If

                        'If (nowTicks - _freeTicks) > ServerSocket.HeartWaitTime * 10000 * ServerSocket.HeartRep Then
                        '    RaiseEvent OnHeartTimeout(Me, EventArgs.Empty)
                        '    If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.HeartTimeout))
                        '    Exit Do
                        'End If

                        'BeginHeart()
                        'If _isDisconnected Then Exit Do
                    Else

                        '检测客户端Socket是否有数据
                        Try
                            If clientSocket.Poll(0, SelectMode.SelectRead) Then
                                bufLen = clientSocket.Available
                                If bufLen = 0 Then
                                    If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.Normal))
                                    Exit Do
                                Else
                                    _isFreeTime = False
                                    _lastTicks = DateTime.Now.Ticks
                                    If _isDisconnected = False Then RaiseEvent OnReceive(Me, New ReceiveEventArgs(bufLen))
                                End If
                            Else
                                '检测客户端空闲状态
                                Try
                                    If (nowTicks - _lastTicks) > (ServerSocket.HeartIdleTime * 10000) Then
                                        If _isFreeTime = False Then
                                            _freeTicks = DateTime.Now.Ticks
                                            _isFreeTime = True
                                            'BeginHeart()
                                            If _isDisconnected = False Then RaiseEvent OnFreeTime(Me, EventArgs.Empty)
                                        End If
                                    Else
                                        _isFreeTime = False
                                    End If
                                Catch ex As ObjectDisposedException

                                Catch ex As NullReferenceException

                                Catch ex As Exception
                                    'Throw ex
                                End Try
                            End If
                        Catch ex As SocketException
                            'socketException(ex.SocketErrorCode)
                        Catch ex As ObjectDisposedException
                            Exit Do
                        Catch ex As Exception
                            'Throw ex
                        End Try

                    End If

                    ''检测客户端空闲状态
                    'Try
                    '    If (nowTicks - _lastTicks) > (ServerSocket.HeartIdleTime) Then
                    '        If _isFreeTime = False Then
                    '            _freeTicks = DateTime.Now.Ticks
                    '            _isFreeTime = True
                    '            BeginHeart()
                    '            'RaiseEvent OnFreeTime(client, EventArgs.Empty)
                    '        End If
                    '    Else
                    '        _isFreeTime = False
                    '    End If
                    'Catch ex As ObjectDisposedException

                    'Catch ex As NullReferenceException

                    'Catch ex As Exception
                    '    Throw ex
                    'End Try

                Catch ex As SocketException
                    'socketException(ex.SocketErrorCode)
                Catch ex As ThreadAbortException
                    Exit Do
                    'Catch ex As Exception
                    '    'Throw ex
                End Try

                Thread.Sleep(100)
            Loop

        End Sub

        'Protected Sub DisconnectEventCallback(ByVal state As Object)
        '    RaiseEvent OnClientDisconnect(Me, state)
        'End Sub


        ''' <summary>
        ''' 发送数据至客户端
        ''' </summary>
        ''' <param name="bytes">要发送的数据</param>
        ''' <returns>返回已发送数据的长度(字节)</returns>
        ''' <remarks></remarks>
        Public Function Send(ByVal bytes() As Byte) As Integer
            If bytes Is Nothing Then Return -1
            Send = 0

            Try

                Send = _socket.Send(bytes, SocketFlags.None)
                If Send = 0 Then
                    If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.Normal))
                    Return -1
                End If
            Catch ex As SocketException
                If ex.SocketErrorCode = SocketError.TimedOut Then
                    Return -1
                End If
                socketException(ex.SocketErrorCode)
            Catch ex As ObjectDisposedException
                Return -1
            Catch ex As Exception
                Throw ex
            Finally
                _isFreeTime = False
                _lastTicks = DateTime.Now.Ticks
            End Try

        End Function
        ''' <summary>
        ''' 发送数据至客户端
        ''' </summary>
        ''' <param name="bytes">要发送的数据</param>
        ''' <param name="length">要发送的数据长度(字节)</param>
        ''' <returns>返回已发送数据的长度(字节)</returns>
        ''' <remarks></remarks>
        Public Function Send(ByVal bytes() As Byte, ByVal length As Integer) As Integer
            If bytes Is Nothing Then Return -1
            Send = 0

            Try

                Send = _socket.Send(bytes, length, SocketFlags.None)
                If Send = 0 Then
                    If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.Normal))
                    Return -1
                End If
            Catch ex As SocketException
                If ex.SocketErrorCode = SocketError.TimedOut Then
                    Return -1
                End If
                socketException(ex.SocketErrorCode)
            Catch ex As ObjectDisposedException
                Return -1
            Catch ex As Exception
                Throw ex
            Finally
                _isFreeTime = False
                _lastTicks = DateTime.Now.Ticks
            End Try

        End Function

        ''' <summary>
        ''' 从客户端读取接收数据
        ''' </summary>
        ''' <param name="length">读取长度(字节)</param>
        ''' <returns>返回byte数组</returns>
        ''' <remarks></remarks>
        Public Function Receive(ByVal length As Integer) As Byte()
            ReDim _buffer(length - 1)

            Try
                If _socket.Receive(_buffer, 0, length, SocketFlags.None) = 0 Then
                    If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.Normal))
                    _buffer = Nothing
                End If

            Catch ex As SocketException
                socketException(ex.SocketErrorCode)
                _buffer = Nothing
            Catch ex As ObjectDisposedException
                _buffer = Nothing
            Catch ex As Exception
                _buffer = Nothing
                Throw ex
            Finally
                _isFreeTime = False
                _lastTicks = DateTime.Now.Ticks
            End Try

            Return _buffer
        End Function

#Region "Not Use"

        'Protected Sub BeginHeart()
        '    Try
        '        ReDim _heartData(_serverSocket.HeartDataClient.Length - 1)
        '        _socket.BeginReceive(_heartData, 0, _heartData.Length, SocketFlags.None, _heartCallback, Nothing)
        '    Catch ex As Exception

        '    End Try
        'End Sub

        'Protected Sub HeartCallback(ByVal ar As IAsyncResult)
        '    If ar.IsCompleted Then
        '        Dim length As Integer = 0

        '        Try
        '            length = _socket.EndReceive(ar)
        '            If length = 0 Then
        '                If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.Normal))
        '                Exit Try
        '            End If

        '            _isFreeTime = False
        '            _lastTicks = DateTime.Now.Ticks

        '            If CheckHeart(_heartData) Then
        '                length = _socket.Send(_serverSocket.HeartDataServer, SocketFlags.None)
        '                If length = 0 Then
        '                    If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.Normal))
        '                    Exit Try
        '                End If
        '            Else
        '                _buffer = _heartData
        '                RaiseEvent OnReceive(Me, New ReceiveEventArgs(_buffer.Length))
        '            End If
        '        Catch ex As NullReferenceException

        '        Catch ex As ObjectDisposedException

        '        Catch ex As SocketException
        '            socketException(ex.SocketErrorCode)
        '        Catch ex As Exception
        '            Throw ex
        '        End Try

        '    End If

        'End Sub
#End Region

        Protected Sub BeginHeart()
            Dim length As Integer = 0
            ReDim _heartData(_serverSocket.HeartDataClient.Length - 1)

            Try
                length = _socket.Receive(_heartData, _heartData.Length, SocketFlags.None)
                If length = 0 Then
                    If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.Normal))
                    Return
                End If

                _isFreeTime = False
                _lastTicks = DateTime.Now.Ticks

                If CheckHeart(_heartData) Then
                    length = _socket.Send(_serverSocket.HeartDataServer, SocketFlags.None)
                    If length = 0 Then
                        If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.Normal))
                        Return
                    End If
                Else
                    _buffer = _heartData
                    If _isDisconnected = False Then RaiseEvent OnReceive(Me, New ReceiveEventArgs(_buffer.Length))
                End If
            Catch ex As NullReferenceException

            Catch ex As ObjectDisposedException

            Catch ex As SocketException
                socketException(ex.SocketErrorCode)
            Catch ex As Exception
                Throw ex
            End Try
        End Sub


#Region "Heart Beat Functions"

        Protected Function CheckHeart(ByVal bytes() As Byte) As Boolean
            If bytes.Length = _serverSocket.HeartDataClient.Length Then
                For i As Integer = 0 To bytes.Length - 1
                    If bytes(i) <> _serverSocket.HeartDataClient(i) Then
                        Return False
                    End If
                Next i
                Return True
            End If
            Return False
        End Function

#End Region

        Friend Sub Close(ByVal timeout As Integer)
            If _isDisconnected Then Return
            'Monitor.Enter(Me)
            Try
                _isDisconnected = True

                _isReceiving = False
                If _thread IsNot Nothing Then
                    '_thread.Abort()
                    _thread = Nothing
                End If

                _socket.Shutdown(SocketShutdown.Both)
                _socket.Close(timeout)
                _socket = Nothing
            Catch ex As SocketException
                socketException(ex.SocketErrorCode)
            Catch ex As Exception
                Throw ex
            Finally
                'Monitor.Exit(Me)
            End Try
        End Sub

        Protected Sub socketException(ByVal errorCode As SocketError)
            'Monitor.Enter(Me)
            Try
                Select Case errorCode
                    Case SocketError.ConnectionReset
                        If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.ConnectionReset))
                    Case SocketError.ConnectionAborted
                        If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.ConnectionAborted))
                        'Me.m_serverSocket.CloseClient(0, Me)
                    Case SocketError.Shutdown
                        If _isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.Shutdown))
                    Case SocketError.TimedOut
                        'If m_isDisconnected = False Then RaiseEvent OnClientDisconnect(Me, New DisconnectArgs(DisconnectType.TimedOut))
                    Case SocketError.HostUnreachable '网卡禁用
                        'RaiseEvent OnHostUnreachable(Me, EventArgs.Empty)
                    Case SocketError.WouldBlock

                    Case SocketError.NotSocket

                    Case SocketError.AddressAlreadyInUse
                        'Me.Close(0)
                        'Throw New AppException("AddressAlreadyInUse")
                    Case SocketError.Interrupted

                    Case SocketError.NotConnected

                    Case Else
                        Throw New Exception(errorCode.ToString())
                End Select
            Finally
                'Monitor.Exit(Me)
            End Try
        End Sub

        'Public Overrides Function Equals(ByVal obj As Object) As Boolean
        '    'Return MyBase.Equals(obj)
        '    'Return _ID = obj.ID
        '    'Equals = _ID = obj.ID
        '    Return _ID.Equals(obj.ID)
        'End Function

        'Public Shared Operator =(ByVal lhs As ClientInfo, ByVal rhs As Object) As Boolean
        '    Return lhs.ID = rhs.ID
        '    'Return lhs.ID.Equals(rhs.ID)
        'End Operator

        'Public Shared Operator <>(ByVal lhs As ClientInfo, ByVal rhs As Object) As Boolean
        '    Return lhs.ID <> rhs.ID
        '    'Return lhs.ID.Equals(rhs.ID)
        'End Operator

    End Class

End Namespace

